﻿using Microsoft.EntityFrameworkCore;
using MiniFox.Components;
using MiniFox.Data.EntityFramework;
using MiniFox.Platform;
using MiniFox.Platform.Models;
using MiniFox.Platform.OAuth2.Providers;
using MiniFox.Utility;


namespace OAuth2.Data
{
    /// <summary>
    /// 
    /// </summary>
    [AutoResolve]
    public class AccessTokenProvider : BaseOAuth2DataProvider, IAccessTokenProvider
    {
        public AccessTokenProvider()
        {
        }

        public AuthorizeToken CreateAuthorizeToken(string appId, string authCode, int accessTokenExpires, int renewTokenExpires)
        {
            SysAuthCode sys_authcode = this.CurrentDbContext.SysAuthCode.SingleOrDefault(c => c.AppId == appId && c.Code == authCode);
            SysAuthToken sys_authToken = this.CurrentDbContext.SysAuthToken.SingleOrDefault(t => t.AppId == appId && t.TicketId == sys_authcode.TicketId);
            DateTime now = DateTime.Now;
            List<BatchOperationItem> batch = new List<BatchOperationItem>();
            if (sys_authToken != null)
            {
                if (sys_authToken.AccessExpireTime < DateTime.Now)
                {
                    sys_authToken.AccessToken = Tools.GenerateRandomHashCode();
                    sys_authToken.RenewTime = now;
                    sys_authToken.RenewExpireTime = now.AddSeconds(renewTokenExpires);
                    sys_authToken.AccessExpireTime = now.AddSeconds(accessTokenExpires);
                    batch.Add(new BatchOperationItem
                    {
                        Entity = sys_authToken,
                        State = EntityState.Modified
                    });
                }
            }
            else
            {
                sys_authToken = new SysAuthToken();
                sys_authToken.AppId = sys_authcode.AppId;
                sys_authToken.AccessToken = Tools.GenerateRandomHashCode();
                sys_authToken.RenewToken = Tools.GenerateRandomHashCode();
                sys_authToken.TicketId = sys_authcode.TicketId;
                sys_authToken.Scope = sys_authcode.Scope;
                sys_authToken.Context = sys_authcode.Context;
                sys_authToken.CreateTime = now;
                sys_authToken.RenewTime = now;
                sys_authToken.RenewExpireTime = now.AddSeconds(renewTokenExpires);
                sys_authToken.AccessExpireTime = now.AddSeconds(accessTokenExpires);
                batch.Add(new BatchOperationItem
                {
                    Entity = sys_authToken,
                    State = EntityState.Added
                });
            }

            batch.Add(new BatchOperationItem
            {
                Entity = sys_authcode,
                State = EntityState.Deleted
            });

            if (this.CommitBatch(batch.ToArray()) <= 0)
            {
                return null;
            }
            return new AuthorizeToken
            {
                AccessToken = sys_authToken.AccessToken,
                RefreshToken = sys_authToken.RenewToken,
                Duration = accessTokenExpires,
                Scope = sys_authcode.Scope
            };
        }

        public AuthorizeToken GetAuthorizeToken(string appId, string refreshToken, int accessTokenExpires, int renewTokenExpires, bool renew)
        {
            var sys_authToken = this.CurrentDbContext.SysAuthToken.SingleOrDefault(t => t.AppId == appId && t.RenewToken == refreshToken);

            SysPassportToken sys_passportToken = this.CurrentDbContext.SysPassportToken.SingleOrDefault(p => p.TicketId == sys_authToken.TicketId);

            if (sys_passportToken == null)
                return null;

            DateTime now = DateTime.Now;


            DateTime expired = sys_passportToken.ExpireTime;
            if (expired < now)
            {
                this.Delete(sys_passportToken);
                return null;
            }

            sys_passportToken.RenewTime = now;
            sys_passportToken.ExpireTime = sys_passportToken.RenewTime.AddSeconds(sys_passportToken.Duration);

            if (renew)
            {
                sys_authToken.AccessToken = Tools.GenerateRandomHashCode();
            }
            sys_authToken.AccessExpireTime = now.AddSeconds(accessTokenExpires);
            sys_authToken.RenewExpireTime = now.AddSeconds(renewTokenExpires);
            sys_authToken.RenewTime = now;
            if (this.Update(sys_passportToken, sys_authToken) <= 0)
                return null;

            return new AuthorizeToken
            {
                AccessToken = sys_authToken.AccessToken,
                RefreshToken = sys_authToken.RenewToken,
                Duration = accessTokenExpires,
                Scope = sys_authToken.Scope
            };
        }

        public AuthorizeToken GetAuthorizeToken(string accessToken)
        {
            var sys_authToken = this.CurrentDbContext.SysAuthToken.SingleOrDefault(t => t.AccessToken == accessToken);

            SysPassportToken sys_passportToken = this.CurrentDbContext.SysPassportToken.SingleOrDefault(p => p.TicketId == sys_authToken.TicketId);

            return new AuthorizeToken
            {
                AccessToken = sys_authToken.AccessToken,
                RefreshToken = sys_authToken.RenewToken,
                Duration = (int)(sys_authToken.AccessExpireTime - sys_authToken.RenewTime).TotalSeconds,
                Scope = sys_authToken.Scope
            };
        }

        public OpenAuthIdentity GetIdentity(string accessToken)
        {
            var sys_authToken = this.CurrentDbContext.SysAuthToken.SingleOrDefault(t => t.AccessToken == accessToken);

            SysPassportToken sys_passportToken = this.CurrentDbContext.SysPassportToken.SingleOrDefault(p => p.TicketId == sys_authToken.TicketId);
            var sys_user = this.CurrentDbContext.SysUser.SingleOrDefault(p => p.UserId == sys_passportToken.UserId);
            return MappingIdentity(sys_user);
        }

        public OpenAuthIdentity GetIdentity(string accessToken, AuthScope authScope)
        {
            OpenAuthIdentity identity = null;
            switch (authScope)
            {
                case AuthScope.Advanced:
                    identity = GetIdentity(accessToken);
                    break;
                case AuthScope.Simple:
                    identity = GetIdentity(accessToken);
                    break;
                case AuthScope.Standard:
                default:
                    identity = GetIdentity(accessToken);
                    break;

            }
            return identity;
        }

        public bool VerifyAuthCode(string appId, string authCode)
        {
            SysAuthCode sys_code = this.CurrentDbContext.SysAuthCode.SingleOrDefault(c => c.AppId == appId && c.Code == authCode);
            return sys_code != null && sys_code.ExpireTime > DateTime.Now;
        }

        public bool VerifyClient(string appId, string secretKey)
        {
            return this.CurrentDbContext.SysAppSecret.Any(a => a.AppId == appId && a.SecretKey == secretKey);
        }

        public bool VerifyRenewToken(string appId, string refreshToken)
        {
            var sys_authToken = this.CurrentDbContext.SysAuthToken.SingleOrDefault(t => t.AppId == appId && t.RenewToken == refreshToken);
            return sys_authToken != null && sys_authToken.RenewExpireTime > DateTime.Now;
        }
    }
}
